package com.linkoog.devtools.dubbo.parser.annotation;

import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAnnotationMemberValue;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassObjectAccessExpression;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiLiteral;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.impl.PsiImplUtil;
import com.linkoog.devtools.dubbo.parser.DubboService;
import com.linkoog.devtools.dubbo.parser.DubboServiceParser;

import java.util.Arrays;
import java.util.List;

public class AnnotationDubboServiceParser implements DubboServiceParser {
    //provider
    public static final List<String> DUBBO_PROVIDER_ANNO_LIST = Arrays.asList(
            "com.alibaba.dubbo.config.annotation.Service",
            "org.apache.dubbo.config.annotation.Service",
            "org.apache.dubbo.config.annotation.DubboService"
    );
    //consumer
    public static final List<String> DUBBO_CONSUMER_ANNO_LIST = Arrays.asList(
            "com.alibaba.dubbo.config.annotation.Reference",
            "org.apache.dubbo.config.annotation.Reference",
            "org.apache.dubbo.config.annotation.DubboReference"
    );

    public static AnnotationDubboServiceParser getInstance(){
        return EXTENSION_POINT_NAME.findExtension(AnnotationDubboServiceParser.class);
    }

    @Override
    public DubboService parse(PsiClass psiClass){
        if (psiClass == null) return null;

        PsiAnnotation psiAnnotation = parseDubboAnno(psiClass, true);
        if (psiAnnotation == null) return null;

        String sourceType = parseSourceType(psiAnnotation);
        String inface = parseInface(psiAnnotation, psiClass);
        if (inface == null) return null;  //解析失败

        String version = parseNameAttribute(psiAnnotation,"version","");
        String group = parseNameAttribute(psiAnnotation,"group","");
        if (version == null || version.startsWith("${")) version = "";

        return new DubboService(sourceType, inface, version, group);
    }

    private String parseSourceType(PsiAnnotation psiAnnotation){
        if (DUBBO_PROVIDER_ANNO_LIST.contains(psiAnnotation.getQualifiedName())){
            return "anno-provider";
        } else if (DUBBO_CONSUMER_ANNO_LIST.contains(psiAnnotation.getQualifiedName())){
            return "anno-consumer";
        }
        return "anno";
    }

    private String parseInface(PsiAnnotation psiAnnotation, PsiClass psiClass){
        String inface = parseNameAttribute(psiAnnotation,"interfaceName",null);
        if (inface != null) return inface;

        // interfaceClass 属性，查找
        String infaceClass = parseNameAttribute(psiAnnotation,"interfaceClass",null);
        if (infaceClass != null) return infaceClass;


        //最后，使用第一个父接口，作为服务接口，否则使用自身
        PsiClass[] interfaces = psiClass.getInterfaces();
        if (interfaces.length > 0){
            inface = interfaces[0].getQualifiedName();
        } else {
            inface = psiClass.getQualifiedName();
        }
        return inface;
    }


    private String parseNameAttribute(PsiAnnotation paramPsiAnnotation, String attrName, String defaultName){
        PsiAnnotationMemberValue psiAnnotationMemberValue = paramPsiAnnotation.findDeclaredAttributeValue(attrName);
        if (psiAnnotationMemberValue == null){
            return defaultName;
        }

        if (psiAnnotationMemberValue instanceof PsiLiteralExpression){
            final Object value = ((PsiLiteral)psiAnnotationMemberValue).getValue();
            if (value instanceof String) return (String)value;
        }else if (psiAnnotationMemberValue instanceof PsiClassObjectAccessExpression){
            PsiClassObjectAccessExpression psiClassObjectAccessExpression = (PsiClassObjectAccessExpression)psiAnnotationMemberValue;
            PsiClassType psiClassType = getComponentClassTypeFromClassObjectAccessExpression(psiClassObjectAccessExpression);
            PsiClass resolve = psiClassType.resolve();
            if (resolve != null){
                return resolve.getQualifiedName();
            }
            return null;
        }

        return psiAnnotationMemberValue.getText().trim();
    }

    public static PsiClassType getComponentClassTypeFromClassObjectAccessExpression(PsiClassObjectAccessExpression componentsClassAccess) {
        // classType is Class<MyComponent>
        PsiClassType classType = (PsiClassType) PsiImplUtil.getType(componentsClassAccess);

        // componentClassType is MyComponent
        return (PsiClassType) classType.getParameters()[0];
    }

    public PsiAnnotation parseDubboAnno(PsiClass psiClass, boolean ignoreSuperClass) {
        PsiAnnotation psiAnnotation = parseDubboAnno(psiClass);
        if (psiAnnotation != null ) {
            return psiAnnotation;
        }

        if (ignoreSuperClass){
            return null;
        }

        PsiClass[] superPsiClassArray = psiClass.getSupers();
        for (PsiClass superPsiClass : superPsiClassArray) {
            psiAnnotation = parseDubboAnno(superPsiClass);
            if (psiAnnotation != null){
                return psiAnnotation;
            }
        }
        return null;
    }

    public PsiAnnotation parseDubboAnno(PsiClass psiClass) {
        for (String classAnnotationQualifiedName : DUBBO_PROVIDER_ANNO_LIST) {
            PsiAnnotation psiAnnotation = psiClass.getAnnotation(classAnnotationQualifiedName);
            if (psiAnnotation != null){
                return psiAnnotation;
            }
        }

        for (String classAnnotationQualifiedName : DUBBO_CONSUMER_ANNO_LIST) {
            PsiAnnotation psiAnnotation = psiClass.getAnnotation(classAnnotationQualifiedName);
            if (psiAnnotation != null){
                return psiAnnotation;
            }
        }
        return null;
    }


}
